package cn.db.service;

import cn.db.dao.OrderDao;
import cn.db.entity.Order;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;

@Service
public class OrderService {

    @Autowired
    private OrderDao orderDao;

    public int insert(Order info){
        orderDao.save(info);
        return 1;
    }

    public Order queryByOrderNo(String orderNo){
        Order query = orderDao.findOrderByOrderNo(orderNo);
        return query;
    }

    public Map<String,Object> queryOrderInfo(String orderCode){
        Map<String,Object> map = new HashMap<>();
        map.put("orderCode",orderCode );
        map.put("orderTime", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));
        double random = Math.random()*1000;
        long l = new Double(random).longValue();
        map.put("orderMoney",500+l );
        return map;
    }

    public List<Map<String,Object>> queryOrderInfoByIdBatch(List<Map<String,String>> orderList){
        List<Map<String,Object>> list = new ArrayList<>();
        Iterator it = orderList.iterator();
        while (it.hasNext()){
            Map<String,Object> map = (Map<String, Object>) it.next();
            map.put("orderTime", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));
            double random = Math.random()*1000;
            long l = new Double(random).longValue();
            map.put("orderMoney",500+l );
            map.put("orderCode",map.get("orderId") );
            map.put("serialNo", map.get("serialNo"));
            list.add(map);
        }
        return list;
    }


    public Map<String,Object> queryOrderBatch(String orderId) throws Exception {
        String serialNo = UUID.randomUUID().toString();

        //CompletableFuture监听结果，相当于FutureTask批量
        CompletableFuture<Map<String,Object>> future = new CompletableFuture<>();
        //绑定业务线程serialNo-orderId
        Request request = new Request();
        request.serialNo = serialNo;
        request.future = future;
        request.orderId = orderId;
        //把数据放入队列中
        queue.add(request);
        return future.get();//没有拿到结果，会阻塞，如果拿到结果，相当于FutureTask.get();
    }

    //封装请求体
    class Request{
        //内部生成的
        String serialNo;
        //查询的订单id，可能会重复
        String orderId;

        CompletableFuture<Map<String,Object>> future;
    }

    //队列（阻塞队里，线程安全、性能考虑，对于增加，删除效率最高）
    LinkedBlockingDeque<Request> queue = new LinkedBlockingDeque<>();

    @PostConstruct
    public void doBusiness(){
        //定时线程池，每隔10ms运行一次
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(1 );
        threadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                //队列中请求的数量
                int size = queue.size();
                //没有请求，直接返回
                if (size == 0){
                    return;
                }
                //根据接口来封装批量参数，两个List,一个组装请求
                List<Map<String,String>> params = new ArrayList();
                List<Request> requests = new ArrayList<>();

                for (int i=0;i<size;i++){
                    //将队列中的元素取出
                    Request request = queue.poll();
                    Map<String,String> map = new HashMap<>();
                    map.put("serialNo",request.serialNo );
                    map.put("orderId",request.orderId );
                    params.add(map);
                    requests.add(request);
                }
                System.out.println("批量处理的数据量："+size);
                //调用批量接口，得到一个批量的接口list
                List<Map<String,Object>> responses = queryOrderInfoByIdBatch(params);
                //批量结果返回到匹配对应的线程serialNo
                for (Request request : requests){
                    String serialNo = request.serialNo;
                    for (Map<String,Object> res: responses){
                        if (serialNo.equals(res.get("serialNo").toString())){
                            request.future.complete(res);
                            break;
                        }
                    }
                }
            }
        }, 100, 10, TimeUnit.MILLISECONDS);

    }
}
